#include "parse_mini_vcf_selected_strain.h"


#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/tr1/tuple.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>
#include <boost/spirit/home/phoenix/algorithm.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using std::string;
using std::cerr;
using std::vector;



BOOST_FUSION_ADAPT_STRUCT(
t_strain_data,
(char, genotype1)
(char, genotype2)
(int,  mapping_quality)
(int,  high_quality_genotype)
(int,  genotype_quality)
(int,  depth)
)


BOOST_FUSION_ADAPT_STRUCT(
t_mini_vcf_selected_strain,
(std::string, ref)
(std::string, alt)
(t_strain_data, strain_data)
)

//header_str = "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT";

#include "qi_parse_error_handler.h"

//struct errorHandlerT  {
//
//    // signature of operator() 
//    //      5 input paramters, returns void
//    template <typename, typename, typename, typename, typename>
//    struct result { typedef void type; };
//
//    
//    template <typename Iter>
//    void operator()(Iter beg, Iter end, Iter errPos, qi::info const &what, unsigned& cnt_line) const
//    {
//        string line = string(beg, end);
//        string line_from_error = string(errPos, end);
//        string line_to_error = string(beg, errPos);
//        string line_to_error_spaces;
//        for (unsigned i = 0; i < line_to_error.length(); ++i)
//            line_to_error_spaces += (line_to_error[i] == '\t') ? '\t' : ' ';
//
//        std::cerr
//                    << "\nError!\n\n\tExpecting '"
//                    << what
//                    << "' here: '" << line_from_error << "'"
//                    << "\n\tLine #" << (cnt_line + 1)
//                    << ", letter #" << (errPos - beg)
//                    << "("  << (end - errPos) << " from the end)\n"
//                    << "[" << line << "]\n "
//                    << line_to_error_spaces << "^\n"
//                    << std::endl;
//    }
//
//};

phoenix::function<errorHandlerT> const errorHandler_selected_strain = errorHandlerT("VCF Selected Strain");


template <typename Iterator>
struct mini_vcf_selected_strain_grammar
: qi::grammar<Iterator, t_mini_vcf_selected_strain()>
{
    mini_vcf_selected_strain_grammar(unsigned count_strains, unsigned strain_index, unsigned& cnt_line_)
    : mini_vcf_selected_strain_grammar::base_type(vcf_selected_strain), cnt_line(cnt_line_)
    {
        using qi::lit;
        using qi::lexeme;
        using ascii::char_;
        using qi::int_;
        using ascii::string;
        using namespace qi::labels;
        using qi::eoi;
        using qi::eol;

        genotype1             %= char_("01234.");  
        genotype2             %= char_("01234.");  
        mapping_quality       %= int_;  
        high_quality_genotype %= int_;  
        genotype_quality      %= int_;  
        depth                 %= int_;  

        strain_data %=          genotype1 > lit("/") > genotype2 
                                > lit(":") >> mapping_quality                 
                                > lit(":") >> high_quality_genotype           
                                > lit(":") >> genotype_quality                
                                > lit(":") >> depth;                           


        tab_delim_str %= +(char_ - '\t');
        tab           =  lit("\t");
        identifier    =  lit(".");
        ignored_str   =  +(char_ - '\t');
        ref           %= tab_delim_str; 
        alt           %= (char_ - '\t') % ',';
        quality       =  ignored_str; 
        filter        =  ignored_str; 
        info          =  ignored_str; 
        format        =  ignored_str;
		ignored_strains   =  ignored_str;
        vcf_selected_strain  %=
                            identifier                  > tab  
                            > ref                       > tab  
                            > alt                       > tab  
                            > quality                   > tab  
                            > filter                    > tab  
                            > info                      
							// > tab  
                            // > format
                            > qi::repeat(strain_index + 1)[tab> ignored_str]
						    > tab > strain_data;
        // give names for debug
        tab                  .name("TAB");
        header               .name("Comment/Header line");
        identifier           .name("Identifier (should be '.')");
        ignored_str          .name("Ignored field");
		ignored_strains		 .name("Ignored field");
        ref                  .name("Reference sequence");
        alt                  .name("Alternative sequence");
        quality              .name("Quality");
        filter               .name("Filter");
        info                 .name("Info");
        //format               .name("Format");
        vcf_selected_strain  .name("VCF");
        strain_data          .name("Strain data"); 
        genotype1            .name("Genotype 1"); 
        genotype2            .name("Genotype 2"); 
        mapping_quality      .name("Mapping quality"); 
        high_quality_genotype.name("High quality genotyp"); 
        genotype_quality     .name("Genotype quality"); 
        depth                .name("Depth"); 

        qi::on_error<qi::fail>                               
        (vcf_selected_strain, ::errorHandler_selected_strain(_1, _2, _3, _4, phoenix::ref(cnt_line)));
    }
    unsigned& cnt_line;
    qi::rule<Iterator, void()>            header ;
    qi::rule<Iterator, std::string()>     tab_delim_str ;
    qi::rule<Iterator, void()>            tab           ;
    qi::rule<Iterator, void()>            identifier    ;
    qi::rule<Iterator, void()>            ignored_str   ;
	qi::rule<Iterator, void()>            ignored_strains   ;
    qi::rule<Iterator, std::string()>     ref           ;
    qi::rule<Iterator, std::string()>     alt           ;
    qi::rule<Iterator, void()>            quality       ;
    qi::rule<Iterator, void()>            filter        ;
    qi::rule<Iterator, void()>            info          ;
    qi::rule<Iterator, void()>            format        ;
    qi::rule<Iterator, t_mini_vcf_selected_strain()>      vcf_selected_strain           ;

    qi::rule<Iterator, t_strain_data()>   strain_data ; 
    qi::rule<Iterator, char()>            genotype1            ; 
    qi::rule<Iterator, char()>            genotype2            ; 
    qi::rule<Iterator, int ()>            mapping_quality      ; 
    qi::rule<Iterator, int ()>            high_quality_genotype; 
    qi::rule<Iterator, int ()>            genotype_quality     ; 
    qi::rule<Iterator, int ()>            depth                ; 
};


sp_vcf_selected_strain_grammar
create_mini_vcf_selected_strain_grammar(unsigned cnt_strains, unsigned strain_index, unsigned& curr_line_number)
{
	return boost::shared_ptr<mini_vcf_selected_strain_grammar<std::string::const_iterator> >(new mini_vcf_selected_strain_grammar<std::string::const_iterator>(cnt_strains, strain_index, curr_line_number));
}


bool parse_mini_vcf_selected_strain(sp_vcf_selected_strain_grammar p_vcf_selected_strain_grammar, std::string::const_iterator& str_beg, string::const_iterator str_end, t_mini_vcf_selected_strain& res)
{
	mini_vcf_selected_strain_grammar<std::string::const_iterator>& vcf_selected_strain_grammar = *p_vcf_selected_strain_grammar;
    return qi::parse(str_beg, str_end, vcf_selected_strain_grammar, res);
}
    


